﻿//------------------------------------------------------------------------------
//  Namespace: FruitVentDesign.UtilWindows
//  
//  Function： N/A
//  Name： WaitingWindow
//  
//  Ver       Time                     Author
//  0.10      2021/6/17 11:39:26     FruitVent
//
//  此代码版权归作者本人FruitVent所有
//  源代码使用协议遵循本仓库的开源协议及附加协议，若本仓库没有设置，则按MIT开源协议授权
//  CSDN博客：https://blog.csdn.net/weixin_39552347
//  源代码仓库：https://gitee.com/fruitvent
//  感谢您的下载和使用
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
using FruitVentDesign.Models;
using System;
using System.Threading;
using System.Windows;
using System.Windows.Input;

namespace FruitVentDesign.UtilWindows
{
    /// <summary>
    /// WaitingWindow.xaml 的交互逻辑
    /// </summary>
    public partial class WaitingWindow : Windows.GasPocket
    {
        public WaitingWindow()
        {
            InitializeComponent();
        }

        #region codes for logical
        private volatile object _result = null;
        public object Result
        {
            get
            {
                if (HasException) { throw new InvalidOperationException("there is an exception occurred, cannot get the result"); }
                return _result;
            }
        }

        public bool HasException { get; private set; }

        private Exception _exception = null;
        public Exception Exception
        {
            get { return _exception; }
            private set
            {
                _exception = value;
                if (_exception == null) { HasException = false; }
                else { HasException = true; }
            }
        }

        /// <summary>
        /// run action in background without reporter
        /// </summary>
        /// <param name="action"></param>
        /// <param name="inSTA"></param>
        /// <returns>return true if run successfully, else return false</returns>
        public bool? WaitingFor(Action action, bool inSTA = false)
        {
            _result = null;
            BeginWaiting();

            Thread thread = new Thread(() =>
            {
                try
                {
                    action.Invoke();
                    WaitingOver();
                }
                catch (Exception error)
                {
                    WaitingOverWithException(error);
                }
            });

            if (inSTA) { thread.SetApartmentState(ApartmentState.STA); }
            thread.IsBackground = true;
            thread.Start();

            return ShowDialog();
        }

        /// <summary>
        /// run action in background with reporter
        /// </summary>
        /// <param name="action"></param>
        /// <param name="inSTA"></param>
        /// <returns>return true if run successfully, else return false</returns>
        public bool? WaitingFor(Action<EventHandler<ReportEventArgs>> action, bool inSTA = false)
        {
            _result = null;
            BeginReport();

            Thread thread = new Thread(() => {
                try
                {
                    action.Invoke(Reporter);
                    WaitingOver();
                }
                catch (Exception error)
                {
                    WaitingOverWithException(error);
                }

            });

            if (inSTA) { thread.SetApartmentState(ApartmentState.STA); }
            thread.IsBackground = true;
            thread.Start();

            return ShowDialog();
        }

        /// <summary>
        /// run Func in background without reporter
        /// result of Func will be set to Result property
        /// </summary>
        /// <typeparam name="T">type of result of Func</typeparam>
        /// <param name="func"></param>
        /// <param name="inSTA"></param>
        /// <returns>return true if run successfully, else return false</returns>
        public bool? WaitingFor<T>(Func<T> func, bool inSTA = false)
        {
            _result = null;
            BeginWaiting();

            Thread thread = new Thread(() => {
                try
                {
                    _result = func.Invoke();
                    WaitingOver();
                }
                catch (Exception error)
                {
                    _result = null;
                    WaitingOverWithException(error);
                }

            });

            if (inSTA) { thread.SetApartmentState(ApartmentState.STA); }
            thread.IsBackground = true;
            thread.Start();

            return ShowDialog();
        }

        /// <summary>
        /// run Func in background with reporter
        /// result of Func will be set to Result property
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="func"></param>
        /// <param name="inSTA"></param>
        /// <returns>return true if run successfully, else return false</returns>
        public bool? WaitingFor<T>(Func<EventHandler<ReportEventArgs>, T> func, bool inSTA = false)
        {
            _result = null;
            BeginReport();

            Thread thread = new Thread(() => {
                try
                {
                    _result = func.Invoke(Reporter);
                    WaitingOver();
                }
                catch (Exception error)
                {
                    WaitingOverWithException(error);
                }

            });

            if (inSTA) { thread.SetApartmentState(ApartmentState.STA); }
            thread.IsBackground = true;
            thread.Start();

            return ShowDialog();
        }

        /// <summary>
        /// begin waiting
        /// </summary>
        private void BeginWaiting()
        {
            progressControl.Visibility = Visibility.Collapsed;

            waitingControl.Visibility = Visibility.Visible;
        }

        /// <summary>
        /// begin to do report
        /// </summary>
        private void BeginReport()
        {
            waitingControl.Visibility = Visibility.Collapsed;

            progressBar.Value = 0;
            progressText.Text = "Preparing...";
            progressControl.Visibility = Visibility.Visible;
        }

        /// <summary>
        ///  handler for report progress
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Reporter(object sender, ReportEventArgs e)
        {
            Dispatcher.Invoke(() =>
            {
                // update progress controls
                if (progressControl.Visibility == Visibility.Visible && !e.IsOver)
                {
                    // set progress bar 
                    double value = (e.Done * 100.0) / e.Total;
                    progressBar.Value = value;
                    //set progress text 
                    progressText.Text = $"{e.Done}/{e.Total}";
                }
            });
        }

        /// <summary>
        /// do something when background task run over
        /// </summary>
        private void WaitingOver()
        {
            Dispatcher.Invoke(() =>
            {
                // over show dialog 
                DialogResult = true;
            });
        }

        /// <summary>
        /// do something when backgroud task run over with exception
        /// </summary>
        /// <param name="error">error occurred in background</param>
        private void WaitingOverWithException(Exception error)
        {
            Dispatcher.Invoke(() =>
            {
                // set error
                Exception = error;
                // over show dialog
                DialogResult = false;
            });
        }

        #endregion

        private void Window_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                DragMove();
            }
        }
    }
}
